package core;

import static org.junit.Assert.*;

import java.util.Calendar;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;


public class ItemTest {

	private Calendar now;
	private Calendar expires;
	private Calendar may4th2012;
	
	private ItemManager _im;
	private RootStorageUnit _rsu;
	private Container _su1;
	private Container _su2;
	
	private Product _p1;
	private Product _p2;
	private Product _p3;
	
	private Item _i1p1;
	private Item _i2p1;
	private Item _i3p1;
	private Item _i1p2;
	private Item _i2p2;
	private Item _i3p2;
	private Item _i1p3;
	private Item _i2p3;
	private Item _i3p3;

	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
	}

	@AfterClass
	public static void tearDownAfterClass() throws Exception {
	}

	@Before
	public void setUp() throws Exception {
		_im = ItemManager.instance();
		_rsu = RootStorageUnit.instance();
		
		_su1 = _rsu.addContainer("su1");
		_su2 = _rsu.addContainer("su2");
		
		now = Calendar.getInstance();
		expires = Calendar.getInstance();
		expires.add(Calendar.MONTH, 30);
		may4th2012 = Calendar.getInstance();
		may4th2012.set(2012, 4, 4);
		
		
		//make a bunch of products
		_p1 = new Product(new Barcode("111111111111", BarcodeType.PRODUCT), "myProduct1", new Amount(1, UnitOfMeasurement.count),30,0);
		_p2 = new Product(new Barcode("111111111112", BarcodeType.PRODUCT), "myProduct2", new Amount(2, UnitOfMeasurement.count),30,0);
		_p3 = new Product(new Barcode("111111111113", BarcodeType.PRODUCT), "myProduct3", new Amount(3, UnitOfMeasurement.count),30,0);
		ProductManager.instance().addProduct(_p1);
		ProductManager.instance().addProduct(_p2);
		ProductManager.instance().addProduct(_p3);
		
		//make a bunch of items
		_i1p1 = _im.createItem(_p1, _rsu);
		_i2p1 = _im.createItem(_p1, _rsu);
		_i3p1 = _im.createItem(_p1, _rsu);
		_i1p2 = _im.createItem(_p2, _rsu);
		_i2p2 = _im.createItem(_p2, _rsu);
		_i3p2 = _im.createItem(_p2, _rsu);
		_i1p3 = _im.createItem(_p3, _su1);
		_i2p3 = _im.createItem(_p3, _su1);
		_i3p3 = _im.createItem(_p3, _su2);
	}

	@After
	public void tearDown() throws Exception {
		ItemManager.setInstance(null);
		RootStorageUnit.setInstance(null);
		ProductManager.setInstance(null);
	}

	@Test
	public void testGetBarcode() {
		assertEquals(_i1p1.getBarcode().getNumberRepresentation(), 400000000008L);
		assertEquals(_i2p1.getBarcode().getNumberRepresentation(), 400000000015L);
		assertEquals(_i3p1.getBarcode().getNumberRepresentation(), 400000000022L);
		assertEquals(_i1p2.getBarcode().getNumberRepresentation(), 400000000039L);
		assertEquals(_i2p2.getBarcode().getNumberRepresentation(), 400000000046L);
		assertEquals(_i3p2.getBarcode().getNumberRepresentation(), 400000000053L);
	}
	
	@Test
	public void testGetProduct(){
		assertEquals(_i1p1.getProduct(), _p1);
		assertEquals(_i2p1.getProduct(), _p1);
		assertEquals(_i3p1.getProduct(), _p1);
		assertEquals(_i1p2.getProduct(), _p2);
		assertEquals(_i2p2.getProduct(), _p2);
		assertEquals(_i3p2.getProduct(), _p2);
	}
	
	@Test
	public void testExpires(){
		//they should expire 30 months from this month
		assertEquals(_i1p1.getExpirationDate().get(Calendar.MONTH), expires.get(Calendar.MONTH));
		assertEquals(_i2p1.getExpirationDate().get(Calendar.MONTH), expires.get(Calendar.MONTH));
		assertEquals(_i3p1.getExpirationDate().get(Calendar.MONTH), expires.get(Calendar.MONTH));
		assertEquals(_i1p2.getExpirationDate().get(Calendar.MONTH), expires.get(Calendar.MONTH));
		assertEquals(_i2p2.getExpirationDate().get(Calendar.MONTH), expires.get(Calendar.MONTH));
		assertEquals(_i3p2.getExpirationDate().get(Calendar.MONTH), expires.get(Calendar.MONTH));
	}
	
	@Test
	public void testGetAmount()
	{
		Amount amt = new Amount(1, UnitOfMeasurement.count);
		Amount amt2 = new Amount (13, UnitOfMeasurement.grams);
		
		_p2.setSize(amt2);
		assertTrue(_i1p2.getAmount().equals(amt2));
		assertFalse(_i1p2.getAmount().equals(amt));
		assertTrue(_i1p1.getAmount().equals(amt));
		assertFalse(_i1p1.getAmount().equals(amt2));
	}
	
	@Test
	public void testCanMoveToContainer(){
		assertTrue("You can move an Item to a container that doesn't house its product", _i1p1.canMoveToContainer(_su2));
		assertTrue("You can move an Item to a container where its product already is", _i1p3.canMoveToContainer(_su2));
	}
	
	@Test
	public void testEquals(){
		assertTrue("Item1 == Item1", _i1p1.equals(_i1p1));
		assertFalse("Item1 != Item2", _i1p1.equals(_i1p2));
		assertFalse("Item2 != Item1", _i1p2.equals(_i1p1));
	}
	
	@Test
	public void testIsValidDate(){
		assertTrue("This is a valid date", Item.isValidDate("02/04/2013"));
		assertTrue("Yesterday is a valid date", Item.isValidDate("02/03/2013"));
		assertTrue("Tomorrow is a valid date", Item.isValidDate("02/05/2013"));
		assertFalse("Before 1980 is an invalid valid date", Item.isValidDate("02/04/1970"));
		assertFalse("Before January 1st, 2000 is invalid", Item.isValidDate("12/31/1999"));
		assertTrue("January 1st, 2000 is valid", Item.isValidDate("01/01/2000"));
		assertFalse("Way in the future is a valid date", Item.isValidDate("02/04/2014"));
		assertFalse("Month has to be > 0", Item.isValidDate("00/04/2013"));
		assertFalse("Month has to be > 0", Item.isValidDate("-3/04/2013"));
		assertFalse("Month has to be <= 12", Item.isValidDate("13/04/2013"));
		assertFalse("Day has to be > 0", Item.isValidDate("10/00/2013"));
		assertFalse("Day has to be < 29, 30, or 31, depending on the month", Item.isValidDate("03/32/2013"));
		assertFalse("Day has to be < 29, 30, or 31, depending on the month", Item.isValidDate("04/31/2013"));
		assertFalse("Day has to be < 29, 30, or 31, depending on the month", Item.isValidDate("02/30/2013"));
		assertFalse("The date needs all the fields", Item.isValidDate("32/2013"));
		assertFalse("The date needs all the fields", Item.isValidDate("32//2013"));
	}
	
	@Test
	public void testChangeEntryDate(){
		_i1p1.changeEntryDate("05/04/2012");
		assertEquals(_i1p1.getEntryDate().get(Calendar.MONTH), may4th2012.get(Calendar.MONTH));
		assertEquals(_i1p1.getEntryDate().get(Calendar.DATE), may4th2012.get(Calendar.DATE));
		assertEquals(_i1p1.getEntryDate().get(Calendar.YEAR), may4th2012.get(Calendar.YEAR));
		
		//try to enter an invalid date
		_i1p1.changeEntryDate("03/2012");
		//the date should not have changed
		assertEquals(_i1p1.getEntryDate().get(Calendar.MONTH), may4th2012.get(Calendar.MONTH));
		assertEquals(_i1p1.getEntryDate().get(Calendar.DATE), may4th2012.get(Calendar.DATE));
		assertEquals(_i1p1.getEntryDate().get(Calendar.YEAR), may4th2012.get(Calendar.YEAR));
	
		Calendar testDate = Calendar.getInstance();
		testDate.set(2012, 4, 4);
		_i1p1.changeEntryDate("03/02/2200");
		assertEquals(_i1p1.getEntryDate().get(Calendar.YEAR), testDate.get(Calendar.YEAR));
		assertEquals(_i1p1.getEntryDate().get(Calendar.DAY_OF_YEAR), 
					testDate.get(Calendar.DAY_OF_YEAR));
		
		_i1p1.changeEntryDate("03/02/1900");
		assertEquals(_i1p1.getEntryDate().get(Calendar.YEAR), testDate.get(Calendar.YEAR));
		assertEquals(_i1p1.getEntryDate().get(Calendar.DAY_OF_YEAR), 
					testDate.get(Calendar.DAY_OF_YEAR));
	}
	
	@Test
	public void testAge(){
		Calendar currentDate = Calendar.getInstance();
		Calendar entryDate = Calendar.getInstance();
		
		_i1p1.changeEntryDate("03/08/2013");
		entryDate = (Calendar) _i1p1.getEntryDate().clone();
		assertEquals(_i1p1.age(), getDaysBetween(entryDate, currentDate));
		
		_i1p1.changeEntryDate("03/01/2013");
		entryDate = (Calendar) _i1p1.getEntryDate().clone();
		assertEquals(_i1p1.age(), getDaysBetween(entryDate, currentDate));
		
		_i1p1.changeEntryDate("02/28/2013");
		entryDate = (Calendar) _i1p1.getEntryDate().clone();
		assertEquals(_i1p1.age(), getDaysBetween(entryDate, currentDate));
		
		_i1p1.changeEntryDate("03/18/2012");
		entryDate = (Calendar) _i1p1.getEntryDate().clone();
		assertEquals(_i1p1.age(), getDaysBetween(entryDate, currentDate));
		
		//across leap year day
		_i1p1.changeEntryDate("03/18/2011");
		entryDate = (Calendar) _i1p1.getEntryDate().clone();
		assertEquals(_i1p1.age(), getDaysBetween(entryDate, currentDate));
	}

	private int getDaysBetween(Calendar first, Calendar second)
	{
		setToMidnight(first);
		setToMidnight(second);
		
		int days = 0;
		while (first.before(second))
		{
			days++;
			first.add(Calendar.DATE, 1);
		}
		return days;
	}
	
	private void setToMidnight(Calendar c)
	{
		c.set(Calendar.HOUR_OF_DAY, 0);
		c.set(Calendar.MINUTE, 0);
		c.set(Calendar.SECOND, 0);
		c.set(Calendar.MILLISECOND, 0);
	}
}
